package com.ubi.core.framework.response;

import com.ubi.core.framework.exception.BatchDeleteException;
import com.ubi.core.framework.exception.BusinessException;
import com.ubi.core.framework.exception.KickException;
import com.ubi.core.framework.exception.ResourceNotFoundException;
import com.ubi.core.framework.exception.UploadFailException;
import com.ubi.core.framework.settings.ModuleEnvironment;
import com.ubi.core.framework.settings.ModuleSettings;
import com.ubi.core.utils.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class ErrorResponseBuilder {

    private static final String CHINESE_COMMA = "，";

    @Autowired
    MessageSource messages;

    @Autowired
    ModuleSettings moduleSettings;

    @Autowired
    @Lazy
    private HttpServletRequest request;

    private Locale getLanguage(HttpServletRequest request) {

        String langInReq = request.getHeader("language");
        if (StringUtils.isBlank(langInReq)) {
            return Locale.getDefault();
        }

        Locale locale = new Locale(langInReq);

        return locale == null ? Locale.getDefault() : locale;
    }

    public ErrorResponse build(Throwable throwable, HttpStatus httpStatus) {
        return this.build(throwable, httpStatus, "server.internal.error");
    }

    public ErrorResponse build(Throwable throwable, HttpStatus httpStatus, String messageKey) {
        String key = StringUtils.isBlank(messageKey) ? "server.internal.error" : messageKey;
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorCode(httpStatus.value());
        errorResponse.setMessage(messages.getMessage(key, null, this.getLanguage(request)));
        if (ModuleEnvironment.TESTING.equals(moduleSettings.getEnvironment())) {
            errorResponse.setExceptionTrace(ExceptionUtils.stackTrace(throwable));
        }
        return errorResponse;
    }

    public ErrorResponse buildAuthenticationError(Throwable throwable, HttpStatus httpStatus) {
        return this.build(throwable, httpStatus, "default.ticket.expire.error");
    }

    public ErrorResponse buildValidationExceptionResponse(MethodArgumentNotValidException ex) {
        ErrorResponse response = new ErrorResponse();
        response.setErrorCode(HttpStatus.BAD_REQUEST.value());
        response.setMessage(messages.getMessage("default.validation.error.message", null, this.getLanguage(request)));
        List<ValidationFieldError> validationFieldErrors = new ArrayList<>();
        for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
            ValidationFieldError validationFieldError = new ValidationFieldError();
            validationFieldError.setFiled(fieldError.getField());
            validationFieldError.setMessage(messages.getMessage(fieldError, this.getLanguage(request)));
            validationFieldErrors.add(validationFieldError);
        }

        response.setData(validationFieldErrors);
        return response;
    }

    public ErrorResponse buildBusinessExceptionResponse(BusinessException businessException) {
        ErrorResponse response = new ErrorResponse();
        response.setErrorCode(HttpStatus.BAD_REQUEST.value());
        response.setMessage(
                messages.getMessage(businessException.getMessage(), businessException.getArgs(), this.getLanguage(request)));
        response.setData(businessException.getData());
        response.setErrorCode(businessException.getErrorCode());
        return response;
    }

    public ErrorResponse buildKickExceptionResponse(KickException businessException) {
        ErrorResponse response = new ErrorResponse();
        response.setErrorCode(HttpStatus.GONE.value());
        response.setMessage(
                messages.getMessage(businessException.getMessage(), businessException.getArgs(), this.getLanguage(request)));
        response.setData(businessException.getData());
        response.setErrorCode(businessException.getErrorCode());
        return response;
    }

    public ErrorResponse buildBatchDeleteExceptionResponse(BatchDeleteException batchDeleteException) {
        ErrorResponse response = new ErrorResponse();
        response.setErrorCode(HttpStatus.BAD_REQUEST.value());

        String summaryMessage = messages
                .getMessage("default.delete.summary.message", new Object[]{batchDeleteException.getTotalDeleteCount()},
                        this.getLanguage(request));
        if (!batchDeleteException.getReferByOthersIdList().isEmpty()) {
            summaryMessage = summaryMessage + CHINESE_COMMA + messages
                    .getMessage("default.delete.summary.reference.message",
                            new Object[]{batchDeleteException.getReferByOthersCount()}, this.getLanguage(request));
        }
        if (!batchDeleteException.getNotExistsIdList().isEmpty()) {
            summaryMessage = summaryMessage + CHINESE_COMMA + messages
                    .getMessage("default.delete.summary.not.exists.message",
                            new Object[]{batchDeleteException.getNotExistsCount()}, this.getLanguage(request));
        }

        response.setMessage(summaryMessage);

        List<DeleteItemError> deleteItemErrors = new ArrayList<>();
        for (Long failedId : batchDeleteException.getReferByOthersIdList()) {
            DeleteItemError deleteItemError = new DeleteItemError();
            deleteItemError.setId(failedId);
            deleteItemError
                    .setMessage(messages.getMessage("default.delete.item.failed.message", null, this.getLanguage(request)));
            deleteItemErrors.add(deleteItemError);
        }

        for (Long notExistsId : batchDeleteException.getNotExistsIdList()) {
            DeleteItemError deleteItemError = new DeleteItemError();
            deleteItemError.setId(notExistsId);
            deleteItemError
                    .setMessage(messages.getMessage("default.delete.item.not.exists.message", null, this.getLanguage(request)));
            deleteItemErrors.add(deleteItemError);
        }

        response.setData(deleteItemErrors);

        return response;
    }

    public ErrorResponse buildResourceNotFoundException(ResourceNotFoundException resourceNotFoundException) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorCode(HttpStatus.BAD_REQUEST.value());
        errorResponse
                .setMessage(messages.getMessage("default.resource.not.found.error.message", null, this.getLanguage(request)));
        return errorResponse;
    }

    public ErrorResponse buildUploadFailExceptionResponse(UploadFailException uploadFailException) {
        ErrorResponse response = new ErrorResponse();
        response.setErrorCode(HttpStatus.OK.value());
        response.setMessage(
                messages.getMessage(uploadFailException.getMessage(), uploadFailException.getArgs(), this.getLanguage(request)));
        return response;
    }
}
